﻿Imports AvianWaves.AvianPlay.ControlManager.ControlFunctions
Imports System.Drawing

Public Class ControlButton
  Private _normalBitmap() As Bitmap = Nothing    ' The image that is normally displayed for this button
  Private _downBitmap() As Bitmap = Nothing      ' The image that is displayed when the finger is down on this button
  Private _inactiveBitmap() As Bitmap = Nothing  ' The image that is displayed when the button is inaccessible
  Private _bitmapIndex As Integer = 0            ' Which bitmap in the array do we use?
  Private _bitmapOrigRect As Rectangle = Rectangle.Empty  ' Original rectangle of the bitmap (the actual size of the bitmap)
  Private _bitmapScaleRect As Rectangle = Rectangle.Empty ' Scaled rectangle of the bitmap based on DPI and 
  Private _functionIndex As Functions            ' An integer passed back to the event listener in the OnClick event, intended for comparison in a table of commands that correspond to what this button does
  Private _functionParameter As Integer = 0      ' An integer passed back to the event listener in the OnClick even, intended to be used as an additional argument for the functionIndex
  Private _lastDPIFactor As Single = 0.0F

  Private _type As ButtonType = ButtonType.Normal

  Private _isDown As Boolean = False
  Private _isActive As Boolean = False
  Private _buttonRectangle As Rectangle = Rectangle.Empty
  Private _hitRectangle As Rectangle = Rectangle.Empty

  Private Shared _oFnt As New Font("Tahoma", 7.0F, FontStyle.Regular)
  Private Shared _oBrush As New SolidBrush(Color.Yellow)
  Private Shared _olPen As New Pen(Color.Red)

  ' The following are share variables that store up-to-date state information needed for various buttons that isn't practical to pass in otherwise
  Private Shared _volumePercent As Byte = 100        ' Volume: 0% to 100%
  Private Shared _isMuted As Boolean = False         ' Are we muted?
  Private Shared _trackSliderMax As Integer = 0      ' Maximum value for the track slider (most likely number of seconds); 0 = can't seek!
  Private Shared _trackSliderPosition As Integer = 0 ' Where is the current position

  Public Shared Property OverlayTextColor() As Color
    Get
      Return _oBrush.Color
    End Get
    Set(ByVal value As Color)
      _oBrush = New SolidBrush(value)
    End Set
  End Property

  Public Shared Property OutlineColor() As Color
    Get
      Return _olPen.Color
    End Get
    Set(ByVal value As Color)
      _olPen = New Pen(value)
    End Set
  End Property

  Public Shared ReadOnly Property OutlinePen() As Pen
    Get
      Return _olPen
    End Get
  End Property

  Public Shared Property VolumePercent() As Byte
    Get
      Return _volumePercent
    End Get
    Set(ByVal value As Byte)
      _volumePercent = value
    End Set
  End Property

  Public Shared Property Mute() As Boolean
    Get
      Return _isMuted
    End Get
    Set(ByVal value As Boolean)
      _isMuted = value
    End Set
  End Property

  Public Shared Property TrackSliderMax() As Integer
    Get
      Return _trackSliderMax
    End Get
    Set(ByVal value As Integer)
      _trackSliderMax = value
    End Set
  End Property

  Public Shared Property TrackSliderPosition() As Integer
    Get
      Return _trackSliderPosition
    End Get
    Set(ByVal value As Integer)
      _trackSliderPosition = value
    End Set
  End Property


  Public Enum ButtonType
    Normal = 0
    Toggle = 1
    Slider = 2
  End Enum

  Public Sub New(ByVal newFunctionIndex As Integer, ByVal newFunctionParameter As Integer)
    Me.SetButton(newFunctionIndex, newFunctionParameter)
  End Sub

  Public Sub SetButton(ByVal newFunctionIndex As Integer, ByVal newFunctionParameter As Integer)
    Me._functionIndex = newFunctionIndex
    Me._functionParameter = newFunctionParameter
    Me.InitButton()
  End Sub

  Public ReadOnly Property Type() As ButtonType
    Get
      Return Me._type
    End Get
  End Property

  Public Property IsActive() As Boolean
    Get
      Return Me._isActive
    End Get
    Set(ByVal value As Boolean)
      Me._isActive = value
    End Set
  End Property

  Public ReadOnly Property IsDown() As Boolean
    Get
      Return Me._isDown
    End Get
  End Property

  Public ReadOnly Property HitRectangle() As Rectangle
    Get
      Return Me._hitRectangle
    End Get
  End Property

  Public ReadOnly Property ButtonRectangle() As Rectangle
    Get
      Return Me._buttonRectangle
    End Get
  End Property

  Public Sub DrawButton(ByVal g As Graphics, ByVal rect As Rectangle, ByVal dpiFactor As Single, Optional ByVal drawOutline As Boolean = False)
    ' Set the button rectangle to the passedin rectangle
    Me._buttonRectangle = New Rectangle(rect.X, rect.Y, rect.Width, rect.Height)

    ' Reset the hit rectangle before we reassign it below
    Me._hitRectangle = Rectangle.Empty

    Select Case Me._type
      Case ButtonType.Slider
        If Me._normalBitmap.Length = 3 Then ' Make sure we have 3 bitmaps! 0=empty, 1=full, 2=thumb cursor
          Dim thumbWidth As Integer = Me._normalBitmap(2).Width * dpiFactor

          ' Draw the empty slider in full
          g.DrawImage(Me._normalBitmap(0), rect, New Rectangle(0, 0, Me._normalBitmap(0).Width, Me._normalBitmap(0).Height), GraphicsUnit.Pixel)

          If Me._isActive AndAlso _trackSliderMax > 0 Then
            ' If we are able, draw the progress
            Dim xOffset As Integer = thumbWidth >> 1          ' Half the width of the thumb image is the xoffset
            Dim xLength As Integer = rect.Width - thumbWidth  ' Length of the total slider
            Dim xSliderPos As Integer = CInt((CDbl(_trackSliderPosition) / CDbl(_trackSliderMax)) * CDbl(xLength))
            Dim bmpWidth As Integer = xOffset + xSliderPos
            If bmpWidth > 0 Then
              Using progBmp As New Bitmap(bmpWidth, rect.Height)
                Using progG As Graphics = Graphics.FromImage(progBmp)
                  progG.DrawImage(Me._normalBitmap(1), New Rectangle(0, 0, rect.Width, rect.Height), New Rectangle(0, 0, Me._normalBitmap(1).Width, Me._normalBitmap(1).Height), GraphicsUnit.Pixel)
                  g.DrawImage(progBmp, rect.X, rect.Y)
                End Using
              End Using
            End If

            ' Draw the thumb button
            Dim thumbRect As New Rectangle(rect.X + xSliderPos, rect.Y, Me._normalBitmap(2).Width * dpiFactor, Me._normalBitmap(2).Height * dpiFactor)
            g.DrawImage(Me._normalBitmap(2), thumbRect, New Rectangle(0, 0, Me._normalBitmap(2).Width, Me._normalBitmap(2).Height), GraphicsUnit.Pixel)

            ' Set the hit rectangle to the thumb region
            Me._hitRectangle = thumbRect
          End If
        End If

      Case ButtonType.Toggle, ButtonType.Normal
        Dim curBmp As Bitmap = Nothing

        If Me._isActive Then
          If Me._isDown Then
            If Me._bitmapIndex < Me._downBitmap.Length Then curBmp = Me._downBitmap(Me._bitmapIndex)
          Else
            If Me._bitmapIndex < Me._normalBitmap.Length Then curBmp = Me._normalBitmap(Me._bitmapIndex)
          End If
        Else
          If Me._bitmapIndex < Me._inactiveBitmap.Length Then curBmp = Me._inactiveBitmap(Me._bitmapIndex)
        End If

        If curBmp IsNot Nothing Then
          If Me._bitmapOrigRect = Rectangle.Empty OrElse Me._bitmapScaleRect = Rectangle.Empty OrElse Me._lastDPIFactor <> dpiFactor Then
            Me._bitmapOrigRect = New Rectangle(0, 0, curBmp.Width, curBmp.Height)
            Me._bitmapScaleRect = New Rectangle(0, 0, curBmp.Width * dpiFactor, curBmp.Height * dpiFactor)

            If rect.Width < Me._bitmapScaleRect.Width OrElse rect.Height < Me._bitmapScaleRect.Height Then
              Dim wDif As Integer = Me._bitmapScaleRect.Width - rect.Width
              Dim hDif As Integer = Me._bitmapScaleRect.Height - rect.Height
              If hDif > wDif Then
                Me._bitmapScaleRect.Width = CInt(CSng(Me._bitmapScaleRect.Width) * CSng(rect.Height / Me._bitmapScaleRect.Height))
                Me._bitmapScaleRect.Height = rect.Height
              Else
                Me._bitmapScaleRect.Height = CInt(CSng(Me._bitmapScaleRect.Height) * CSng(rect.Height / Me._bitmapScaleRect.Height))
                Me._bitmapScaleRect.Width = rect.Width
              End If
            End If
          End If

          Dim brect As Rectangle = New Rectangle(rect.X, rect.Y, Me._bitmapScaleRect.Width, Me._bitmapScaleRect.Height)

          If rect.Width > Me._bitmapScaleRect.Width Then
            brect.X += (rect.Width - brect.Width) >> 1
          End If

          If rect.Height > Me._bitmapScaleRect.Height Then
            brect.Y += (rect.Height - brect.Height) >> 1
          End If

          g.DrawImage(curBmp, brect, Me._bitmapOrigRect, GraphicsUnit.Pixel)

          Me._hitRectangle = rect
        End If

        ' Initialize overlay variables
        Dim overlayStr As String = String.Empty

        ' Do we have any overlays to put over the button?
        Select Case Me._functionIndex
          Case Functions.SkipAhead, Functions.SkipBack
            Dim showStyle As Integer = Settings.HowToShowButtonTimeArguments

            Select Case showStyle
              Case 2, 3
                Dim iHours As Integer = Me._functionParameter / 3600      ' Divide out hours
                Dim remainder As Integer = Me._functionParameter Mod 3600 ' Get the remainder of minutes+seconds
                Dim iMinutes As Integer = remainder / 60                  ' Divide out minutes
                Dim iSeconds As Integer = remainder Mod 60                ' Get the remainder, which is the left over seconds

                If showStyle = 2 Then
                  If iHours > 0 Then
                    overlayStr = String.Format("{0:0}:{1:00}:{2:00}", iHours, iMinutes, iSeconds)
                  Else
                    overlayStr = String.Format("{0:0}:{1:00}", iMinutes, iSeconds)
                  End If
                Else
                  If iHours > 0 Then
                    overlayStr &= iHours.ToString & Language.GetLanguageString("OverlayHourAbbreviation")
                  End If

                  If iMinutes > 0 Then
                    overlayStr &= iMinutes.ToString & Language.GetLanguageString("OverlayMinuteAbbreviation")
                  End If

                  overlayStr &= iSeconds.ToString & Language.GetLanguageString("OverlaySecondAbbreviation")
                End If

              Case 1
                overlayStr = Me._functionParameter & Language.GetLanguageString("OverlaySecondAbbreviation")
              Case Else
                overlayStr = Me._functionParameter
            End Select


          Case Functions.VolToggle
            If _isMuted Then
              overlayStr = Language.GetLanguageString("Mute")
            Else
              overlayStr = _volumePercent.ToString & "%"
            End If

          Case Functions.JumpToEntry
            overlayStr = Me._functionParameter.ToString

        End Select

        ' Draw the overlay
        If overlayStr <> String.Empty Then
          Dim overlaySize As SizeF = g.MeasureString(overlayStr, _oFnt)
          g.DrawString(overlayStr, _oFnt, _oBrush, rect.X + ((rect.Width - overlaySize.Width) >> 1), rect.Y + (rect.Height - overlaySize.Height))
        End If

        ' Draw the outline
        If drawOutline Then
          g.DrawRectangle(_olPen, rect)
        End If
    End Select
  End Sub

  Public ReadOnly Property FunctionIndex() As Integer
    Get
      Return Me._functionIndex
    End Get
  End Property

  ' For multi-function buttons, this is the current function that is visible or else the single-button function
  Public ReadOnly Property FunctionIndexCurrent() As Integer
    Get
      Select Case Me._functionIndex
        ' Look for multi-function buttons
        Case Functions.StopPlayPause
          Select Case Me._bitmapIndex
            Case 0
              Return Functions.Play
            Case 1
              Return Functions.Pause
            Case Else
              Return Functions.Stop
          End Select
        Case Else ' Return the index for single function buttons
          Return Me.FunctionIndex()
      End Select
    End Get
  End Property

  Public ReadOnly Property FunctionParameter() As Integer
    Get
      Return Me._functionParameter
    End Get
  End Property

  Public ReadOnly Property FunctionString() As String
    Get
      If Me._functionParameter <> 0 OrElse ControlFunctions.IsControlFunctionWithParamter(Me._functionIndex) Then
        Return CInt(Me._functionIndex).ToString & ":" & Me._functionParameter.ToString
      Else
        Return CInt(Me._functionIndex).ToString
      End If
    End Get
  End Property

  Public Sub ButtonDown(ByVal mouseX As Integer)
    Me._isDown = True
  End Sub

  Public Sub ButtonUp(ByVal mouseX As Integer)
    If Me._functionIndex = Functions.TrackPositionSlider Then
      ButtonDrag(mouseX) ' Perform one last 'drag' operation for the trackposition slider (make sure our mouse coords are up to date)
    End If
    Me._isDown = False
  End Sub

  Public Sub ButtonDrag(ByVal mouseX As Integer)
    ' Handle the track position slider when a drag event occurs
    If Me._functionIndex = Functions.TrackPositionSlider Then
      If Me._hitRectangle <> Rectangle.Empty AndAlso Me._buttonRectangle <> Rectangle.Empty Then
        Dim xLength As Integer = Me._buttonRectangle.Width - Me._hitRectangle.Width
        Dim xPos As Integer = mouseX - Me._buttonRectangle.X
        Dim halfThumb As Integer = Me._hitRectangle.Width >> 1
        If xPos < Me._buttonRectangle.X + halfThumb Then xPos = Me._buttonRectangle.X + halfThumb
        If xPos > xLength + halfThumb Then xPos = xLength + halfThumb
        _trackSliderPosition = CInt(Math.Round((CDbl(xPos - halfThumb) / CDbl(xLength)) * CDbl(_trackSliderMax)))
      End If
    End If
  End Sub

  Public Sub ButtonClear() ' Used to clear the 'isdown' state without any other functionality (which ButtonUp may have in the future)
    Me._isDown = False
  End Sub

  Public Sub PlayStateCallback(ByVal isReady As Boolean, ByVal fileIsLoaded As Boolean, ByVal isWait As Boolean, ByVal isPlaying As Boolean, ByVal isPaused As Boolean, ByVal isStream As Boolean, ByVal playlistCount As Integer)
    ' Set the active state
    Me.IsActive = GetControlFunctionIsActive(Me._functionIndex, Me._functionParameter, isReady, fileIsLoaded, isWait, isPlaying, isPaused, isStream, playlistCount)

    ' Set the bitmap shown for multifunction buttons
    Select Case Me._functionIndex
      Case Functions.StopPlayPause
        Select Case GetControlFunctionFromMultiFunction(Me._functionIndex, isReady, isWait, isPlaying, isPaused, isStream, False)
          Case Functions.Play
            Me._bitmapIndex = 0
          Case Functions.Pause
            Me._bitmapIndex = 1
          Case Functions.Stop
            Me._bitmapIndex = 2
        End Select

    End Select
  End Sub

  Private Sub ClearBitmap(ByVal bmps() As Bitmap)
    For i As Integer = 0 To bmps.Length - 1
      If bmps(i) IsNot Nothing Then
        bmps(i) = Nothing
      End If
    Next
  End Sub

  Private Sub ClearBitmaps()
    If Me._normalBitmap IsNot Nothing Then ClearBitmap(Me._normalBitmap)
    If Me._downBitmap IsNot Nothing Then ClearBitmap(Me._downBitmap)
    If Me._inactiveBitmap IsNot Nothing Then ClearBitmap(Me._inactiveBitmap)
    Me._normalBitmap = Nothing
    Me._downBitmap = Nothing
    Me._inactiveBitmap = Nothing
    Me._bitmapOrigRect = Rectangle.Empty
    Me._bitmapScaleRect = Rectangle.Empty
  End Sub

  Public Sub Clear()
    Me.ClearBitmaps()
    Me._functionIndex = Functions.NONE
    Me._functionParameter = 0
    Me._type = ButtonType.Normal
  End Sub

  ''' <summary>
  ''' Returns a deep copy of this object.  The returned object will be logically identical to the source object, but will share no references.
  ''' </summary>
  Public Function Copy() As ControlButton
    Return New ControlButton(Me._functionIndex, Me._functionParameter)
  End Function

  Private Sub InitButton()
    Me.ClearBitmaps()

    ReDim Me._normalBitmap(0)
    ReDim Me._downBitmap(0)
    ReDim Me._inactiveBitmap(0)
    Me._bitmapIndex = 0
    Me._type = ButtonType.Normal
    Me._isDown = False
    Me._isActive = False

    Select Case _functionIndex
      Case Functions.NONE ' Start of single function buttons
        Exit Select

      Case Functions.Stop
        Me._normalBitmap(0) = Skin.SkinBitmapButton_StopNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_StopDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_StopInactive

      Case Functions.Play
        Me._normalBitmap(0) = Skin.SkinBitmapButton_PlayNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_PlayDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_PlayInactive

      Case Functions.Pause
        Me._normalBitmap(0) = Skin.SkinBitmapButton_PauseNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_PauseDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_PauseInactive

      Case Functions.PreviousRewind
        Me._normalBitmap(0) = Skin.SkinBitmapButton_RewindNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_RewindDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_RewindInactive

      Case Functions.NextForward
        Me._normalBitmap(0) = Skin.SkinBitmapButton_ForwardNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_ForwardDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_ForwardInactive

      Case Functions.SkipAhead
        Me._normalBitmap(0) = Skin.SkinBitmapButton_SkipAheadNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_SkipAheadDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_SkipAheadInactive

      Case Functions.SkipBack
        Me._normalBitmap(0) = Skin.SkinBitmapButton_SkipBackNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_SkipBackDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_SkipBackInactive

      Case Functions.MemorizeSong
        Me._normalBitmap(0) = Skin.SkinBitmapButton_MemorizeNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_MemorizeDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_MemorizeInactive

      Case Functions.JumpToEntry
        Me._normalBitmap(0) = Skin.SkinBitmapButton_JumpToEntryNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_JumpToEntryDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_JumpToEntryInactive

      Case Functions.VolToggle
        Me._normalBitmap(0) = Skin.SkinBitmapButton_VolumeToggleNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_VolumeToggleDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_VolumeToggleInactive

      Case Functions.ScenePrevious
        Me._normalBitmap(0) = Skin.SkinBitmapButton_PreviousSceneNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_PreviousSceneDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_PreviousSceneInactive

      Case Functions.SceneNext
        Me._normalBitmap(0) = Skin.SkinBitmapButton_NextSceneNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_NextSceneDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_NextSceneInactive

      Case Functions.OpenAudioFile
        Me._normalBitmap(0) = Skin.SkinBitmapButton_OpenAudioNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_OpenAudioDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_OpenAudioInactive

      Case Functions.OpenPlaylistFile
        Me._normalBitmap(0) = Skin.SkinBitmapButton_OpenPlaylistNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_OpenPlaylistDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_OpenPlaylistInactive

      Case Functions.OpenURL
        Me._normalBitmap(0) = Skin.SkinBitmapButton_OpenURLNormal
        Me._downBitmap(0) = Skin.SkinBitmapButton_OpenURLDown
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_OpenURLInactive


      Case Functions.StopPlayPause ' Start of multi-function buttons
        ReDim Me._normalBitmap(2)
        ReDim Me._downBitmap(2)
        ReDim Me._inactiveBitmap(2)
        Me._normalBitmap(0) = Skin.SkinBitmapButton_PlayNormal ' PLAY
        Me._downBitmap(0) = Skin.SkinBitmapButton_PlayDown ' PLAY
        Me._inactiveBitmap(0) = Skin.SkinBitmapButton_PlayInactive ' PLAY
        Me._normalBitmap(1) = Skin.SkinBitmapButton_PauseNormal ' PAUSE
        Me._downBitmap(1) = Skin.SkinBitmapButton_PauseDown  ' PAUSE
        Me._inactiveBitmap(1) = Skin.SkinBitmapButton_PauseInactive  ' PAUSE
        Me._normalBitmap(2) = Skin.SkinBitmapButton_StopNormal ' STOP
        Me._downBitmap(2) = Skin.SkinBitmapButton_StopDown ' STOP
        Me._inactiveBitmap(2) = Skin.SkinBitmapButton_StopInactive ' STOP



      Case Functions.TrackPositionSlider ' Start of sliders
        Me._type = ButtonType.Slider
        ReDim Me._normalBitmap(2)
        Me._normalBitmap(0) = Skin.SkinBitmapButton_TrackSlider_Empty
        Me._normalBitmap(1) = Skin.SkinBitmapButton_TrackSlider_Full
        Me._normalBitmap(2) = Skin.SkinBitmapButton_TrackSlider_Thumb
    End Select
  End Sub

End Class
